#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "Util.hpp"
#include"Log.hpp"

const std::string src_path = "data/input/";
const std::string output_file = "data/output/dest.txt";

enum ErrCode
{
    EnumFileNameErr = 1,
    ParseHtmlErr,
    SaveHtmlErr,
};
class DocInfo
{
public:
    std::string _title;
    std::string _content;
    std::string _url;
};

bool EnumFileName(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo> *html_documents);
bool SaveHtml(const std::vector<DocInfo> &html_documents, const std::string &output_file);
int main()
{
    std::vector<std::string> files_list;
    // 第一步 把搜索范围src_path内的所有html的路径+文件名放到 files_list中
    if (!EnumFileName(src_path, &files_list))
    {
        lg(_Error,"%s","enum filename err!");
        //std::cerr << "enum filename err!" << std::endl;
        exit(EnumFileNameErr);
    }

    // 第二步 将files_list中的文件打开,读取并解析为DocInfo后放到 web_documents中
    std::vector<DocInfo> html_documents;
    if (!ParseHtml(files_list, &html_documents))
    {
        lg(_Error,"%s","parse html err!");
        //std::cerr << "parse html err!" << std::endl;
        exit(ParseHtmlErr);
    }

    // 第三步 将web_documents的信息写入到 output_file文件中, 以\3为每个文档的分隔符
    if (!SaveHtml(html_documents, output_file))
    {
        lg(_Error,"%s","save html err!");
        //std::cerr << "save html err!" << std::endl;
        exit(SaveHtmlErr);
    }

}

bool EnumFileName(const std::string &src_path, std::vector<std::string> *files_list)
{
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    if (!fs::exists(root_path)) // 判断路径是否存在
    {
        lg(_Fatal,"%s%s",src_path.c_str()," is not exist");
        //std::cerr << src_path << " is not exist" << std::endl;
        return false;
    }

    // 定义一个空迭代器,用来判断递归是否结束
    fs::recursive_directory_iterator end;
    // 递归式遍历文件
    for (fs::recursive_directory_iterator it(src_path); it != end; it++)
    {
        if (!fs::is_regular(*it))
            continue; // 保证是普通文件
        if (it->path().extension() != ".html")
            continue; // 保证是.html文件

        // std::cout<<"debug: "<<it->path().string()<<std::endl;
        files_list->push_back(it->path().string()); // 插入的都是合法 路径+.html文件名
    }

    return true;
}

// 静态函数只在当前文件内有效,都是ParseHtml的子函数
static bool ParseTitle(const std::string &html_file, std::string *title);
static bool ParseContent(const std::string &html_file, std::string *content);
static bool ParseUrl(const std::string &html_file_path, std::string *url);

static void ShowDcoinfo(const DocInfo &doc_info)
{
    std::cout << "title: " << doc_info._title << std::endl;
    std::cout << "url: " << doc_info._url << std::endl;
    std::cout << "content: " << doc_info._content << std::endl;
}
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo> *html_documents)
{
    for (const std::string &html_file_path : files_list)
    {
        // 第一步 遍历files_list,根据路径+文件名,读取html文件内容
        std::string html_file;
        if (!ns_util::FileUtil::ReadFile(html_file_path, &html_file))
        {
            lg(_Error,"%s","ReadFile err!");
            //std::cerr << "ReadFile err!" << std::endl;
            continue;
        }
        DocInfo doc_info;
        // 第二步 解析html文件,提取title
        if (!ParseTitle(html_file, &doc_info._title))
        {
            lg(_Error,"%s%s","ParseTitle err! ",html_file_path.c_str());
            //std::cerr << "ParseTitle err! "<<html_file_path << std::endl;
            continue;
        }
        // 第三步 解析html文件,提取content(去标签)
        if (!ParseContent(html_file, &doc_info._content))
        {
            lg(_Error,"%s","ParseContent err!");
            //std::cerr << "ParseContent err!" << std::endl;
            continue;
        }
        // 第四步 解析html文件,构建url
        if (!ParseUrl(html_file_path, &doc_info._url))
        {
            lg(_Error,"%s","ParseUrl err!");
            //std::cerr << "ParseUrl err!" << std::endl;
            continue;
        }

        // 解析html文件完毕,结果都保存到了doc_info中
        // ShowDcoinfo(doc_info);
        html_documents->push_back(std::move(doc_info)); // 尾插会拷贝,效率不高,使用move
    }
    lg(_Info,"%s","ParseHtml success!");
    return true;
}
//  asdads<title>......</title>
static bool ParseTitle(const std::string &html_file, std::string *title)
{
    size_t left = html_file.find("<title>");
    if (left == std::string::npos)
        return false;
    size_t right = html_file.find("</title>");
    if (right == std::string::npos)
        return false;

    int begin = left + std::string("<title>").size();
    int end = right;
    // 截取[begin,end-1]内的子串就是标题内容
    if (end-begin<0)
    {
        lg(_Error,"%s%s%s","ParseTitle:",output_file.c_str(),"has no title");
        //std::cerr << "ParseTitle:" << output_file << "has no title" << std::endl;
        return false;
    }

    std::string str = html_file.substr(begin, end - begin);
    //std::cout << "get a title: " << str << std::endl;
    *title = str;
    return true;
}
static bool ParseContent(const std::string &html_file, std::string *content)
{
    // 利用简单状态机完成去标签工作
    enum Status
    {
        Lable,
        Content
    };

    Status status = Lable;
    for (char ch : html_file)
    {
        switch (status)
        {
        case Lable:
            if (ch == '>')
                status = Content;
            break;
        case Content:
            if (ch == '<')
                status = Lable;
            else
            {
                // 不保留html文本中自带的\n,防止后续发生冲突
                if (ch == '\n')
                    ch = ' ';
                content->push_back(ch);
            }
            break;
        default:
            break;
        }
    }

    return true;
}
static bool ParseUrl(const std::string &html_file_path, std::string *url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_84_0/doc/html";
    std::string url_tail = html_file_path.substr(src_path.size());

    *url = url_head + "/" + url_tail;
    return true;
}

//doc_info内部用\3分隔,doc_info之间用\n分隔
bool SaveHtml(const std::vector<DocInfo> &html_documents, const std::string &output_file)
{
    const char sep = '\3';
    std::ofstream out(output_file, std::ios::out | std::ios::binary|std::ios::trunc);
    if (!out.is_open())
    {
        lg(_Fatal,"%s%s%s","SaveHtml:",output_file.c_str()," open err!");
        //std::cerr << "SaveHtml:" << output_file << " open err!" << std::endl;
        return false;
    }

    for(auto &doc_info:html_documents)
    {
        std::string outstr;
        outstr += doc_info._title;
        outstr += sep;
        outstr += doc_info._content;
        outstr += sep;
        outstr+= doc_info._url;
        outstr+='\n';

        out.write(outstr.c_str(),outstr.size());
    }
    out.close();
    lg(_Info,"%s","SaveHtml success!");
    return true;
}